#pragma once
#include <utility>

#ifdef PLATFORM_WINDOWS
	#include <windows.h>
#endif // PLATFORM_WINDOWS


#if PLATFORM_WINDOWS || PLATFORM_DURANGO
	#if PLATFORM_WINDOWS
		#define GSLoadLibrary(libName) ::LoadLibraryA(libName)
	#elif PLATFORM_DURANGO
		#define GSLoadLibrary(libName) ::LoadLibraryExA(libName, 0, 0)
	#endif

	#define GSGetCurrentModule() ::GetModuleHandle(nullptr)
	#define GSSharedLibrarySupported true
	#define GSSharedLibraryPrefix    ""
	
	#ifdef _DEBUG
		#define GSSharedLibraryExtension "_d.dll"
	#else
		#define GSSharedLibraryExtension ".dll"
	#endif

	#define GSGetProcAddress(libHandle, procName) ::GetProcAddress((HMODULE)(libHandle), procName)
	#define GSFreeLibrary(libHandle)              ::FreeLibrary((HMODULE)(libHandle))
#endif

#define GSLibraryDefName(libName)               GSSharedLibraryPrefix libName GSSharedLibraryExtension
#define GSLoadLibraryDefName(libName)           GSLoadLibrary(GSLibraryDefName(libName))

class CGSLibrary
{
public:
	CGSLibrary(const char* szLibraryPath)
		: m_hModule(nullptr)
	{
		if (szLibraryPath != nullptr)
		{
			m_hModule = GSLoadLibrary(szLibraryPath);
		}
	}

	CGSLibrary(const CGSLibrary& other) = delete;

	CGSLibrary(CGSLibrary&& other)
		: m_hModule(std::move(other.m_hModule))
	{
		other.m_hModule = nullptr;
	}

	~CGSLibrary()
	{
		Free();
	}

	void Free()
	{
		if (m_hModule != nullptr)
		{
			GSFreeLibrary(m_hModule);
			m_hModule = nullptr;
		}
	}

	void ReleaseOwnership() { m_hModule = nullptr; }
	bool IsLoaded() const { return m_hModule != nullptr; }

	template<typename TProcedure>
	TProcedure GetProcedureAddress(const char* szName)
	{
		return (TProcedure)GSGetProcAddress(m_hModule, szName);
	}

	void Set(const char* szLibraryPath)
	{
		if (m_hModule != nullptr)
		{
			GSFreeLibrary(m_hModule);
			m_hModule = nullptr;
		}

		if (szLibraryPath != nullptr)
		{
			m_hModule = GSLoadLibrary(szLibraryPath);
		}
	}

	HMODULE m_hModule;
};
